package org.pyant.tasks;

import java.io.File;
import java.io.IOException;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Execute;
import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
import org.apache.tools.ant.taskdefs.LogStreamHandler;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.Path;

/**
 * @author Ron Smith
 *
 * Base class for Python Ant tasks.
 * Tasks inheriting from this task support the following attributes:
 * <ul>
 * <li>python - Use to set the Python executable to be used.  Defaults to "python".
 * <li>pythonpath  - The PYTHONPATH to be used
 * <li>failonerror - If true, fail if any errors occurred
 * <li>optimize - The Python optimize level to use.  0 (no optimization), 1, or 2.
 * </ul>
 */
public class PythonBaseTask extends Task
{
	// PYTHONPATH to be used
	protected Path pythonpath = null;
	
	// Python executable
	protected String python = "python";

	// If true, fail on errors as a result of this task executing
	protected boolean failonerror=true;

	// Python optimize level to be used
	// 0: no optimization
	// 1: -O
	// 2: -OO
	protected int optimize = 0;
	
	private Commandline cmdline = null;
	
	/**
	 * Set the PYTHONPATH as an environment variable into the given Execute task
	 */
	protected void setPythonPathInExecute(Execute runner)
	{
		if(pythonpath != null)
		{
			Project project = getProject();
			project.log("pythonpath: " + pythonpath);//, Project.MSG_DEBUG);
			String envSetting = "PYTHONPATH=" + pythonpath;
			String[] environment = new String[] {envSetting};
			runner.setEnvironment(environment);
		}
	}

	/**
	 * Set the PYTHONPATH as a string
	 * @param pythonpath The pythonpath to set.
	 */
	public void setPythonpath(Path pythonpath)
	{
		this.pythonpath = pythonpath;
	}
	
	/**
	 * Set the PYTHONPATH as an Ant path reference.
	 * @param pathref
	 */
	public void setPythonpathref(String pathref)
	{
		Project project = getProject();
		
		this.pythonpath = (Path)project.getReference(pathref);
	}

	public String getPython()
	{
		return python;
	}
	
	public void setPython(String python)
	{
		this.python = python;
	}
	
	/**
	 * Convert file paths to be forward slashes for Python.
	 * Windows: Why oh why would you choose the universal
	 * escape character as your file delimeter?
	 */
	protected String fixFilePath(String path)
	{
		return(path.replace('\\', '/'));
	}
	
	public void setFailonerror(boolean failonerror)
	{
		this.failonerror = failonerror;
	}

	/**
	 * Execute the python script.
	 * This function will construct the command line, set up the environment,
	 * and execute the Python script.  The particulars are expected to be set
	 * up by derived classes.
	 * @param project - Ant project
	 * @param workingDirectory - Working directory to execute from
	 * @param failMsg - Message to be reported upon execution failure
	 */
	protected void executeScript(Project project,
		File workingDirectory, String failMsg)
	{
		Commandline cmdline = getCommandline();
		
		prepCommandline();
		
		Execute runner = createExecute(project, cmdline,
			workingDirectory);
		
		execExecuteTask(failMsg, runner);
	}

	/**
	 * Execute the given Execute task.
	 * @param failMsg
	 * @param runner
	 */
	protected void execExecuteTask(String failMsg, Execute runner)
	{
		try
		{
			int res = runner.execute();
			if(res != 0)
			{
				failureOcurred(failMsg);
			}
		}
		catch(IOException ie)
		{
			throw new BuildException("Error executing task", ie, getLocation());
		}
	}

	/**
	 * Create the Execute task to be run.
	 * @param project - Ant project
	 * @param cmdline - Commandline to be executed
	 * @param workingDirectory - Working directory to execute from
	 * @return Execute task
	 */
	protected Execute createExecute(Project project, Commandline cmdline,
		File workingDirectory)
	{
		
		ExecuteStreamHandler streamHandler = createStreamHandler();
		
		Execute runner = new Execute(streamHandler, null);
		
		if(workingDirectory != null)
		{
			runner.setWorkingDirectory(workingDirectory);
		}
		
		setPythonPathInExecute(runner);
		
		runner.setAntRun(project);
		runner.setCommandline(cmdline.getCommandline());
		return runner;
	}

	protected ExecuteStreamHandler createStreamHandler()
	{
		LogStreamHandler streamHandler = new LogStreamHandler(this, Project.MSG_INFO,
							Project.MSG_WARN);
		return streamHandler;
	}
	
	protected Commandline getCommandline()
	{
		if(this.cmdline == null)
		{
			this.cmdline = createCommandline();
		}
		return this.cmdline;
	}
	
	protected Commandline createCommandline()
	{
		Commandline cmdline = new Commandline();
		return cmdline;
	}
	
	protected void prepCommandline()
	{
		cmdline.setExecutable(this.python);
		
		addCommandlineArgs(cmdline);
	}

	protected void addCommandlineArgs(Commandline cmdline)
	{
		setOptimizeArgument(cmdline);		
	}
	
	protected void failureOcurred(String msg)
	{
		log("failureOcurred", Project.MSG_DEBUG);
		if(failonerror == true)
		{
			throw new BuildException(msg, getLocation());
		}
		else
		{
			log(msg, Project.MSG_ERR);
		}	
	}

	public void setOptimize(int optimize)
	{
		this.optimize = optimize;
	}

	protected void setOptimizeArgument(Commandline cmdline)
	{
		String optimizeString = null;
		if(optimize == 1)
			optimizeString = "-O";
		else if(optimize == 2)
			optimizeString = "-OO";
		
		if(optimizeString != null)
			cmdline.createArgument(true).setValue(optimizeString);
	}
}
